#pragma once

#include "common/json.h"
#include "common/file.h"
#include "util.h"


namespace evm { namespace http {

using namespace std;

struct Response {
    int code{200};
    Headers headers;
    string body;
    Json json;

    string file_name;  // if not empty, means sending file stream
    shared_ptr<File> file;

    Response () {}

    explicit Response(int code) : code(code) {}

    explicit Response(int code, const string& body) : code(code), body(body) {}

    explicit Response(int code, Json j) : code(code), json(std::move(j)) {
        set_header("Content-Type", "application/json");
    }

    Response(Json j): Response(200, j) {}

    Response(int code, std::initializer_list<Json> j): Response(code, Json(j)) {}

    Response(std::initializer_list<Json> j): Response(200, j) {}

    Response(Response&& r) {
        *this = std::move(r);
    }

    // Response &operator =(const Response &r) = delete;

    Response& operator=(Response&& r) noexcept {
        code = r.code;
        headers = std::move(r.headers);
        body = std::move(r.body);
        json = std::move(r.json);
        file_name = std::move(r.file_name);
        return *this;
    }

    void clear() {
        code = 200;
        headers.clear();
        body.clear();
        json = nullptr;
        file_name.clear();
    }

    void write(const string& body_part) {
        body += body_part;
    }

    /*void json(Json j) {
        body = j.dump();
        set_header("Content-Type", "application/json");
    }*/

    void set_header(string field, string value) {
        headers.erase(field);
        headers.emplace(std::move(field), std::move(value));
    }

    void add_header(string field, string value) {
        headers.emplace(std::move(field), std::move(value));
    }

    const string& get_header(const string& field) {
        return http::get_header(headers, field);
    }

    bool has_header(const string& field) const {
        return headers.count(field);
    }

    static Response recv_file(int code, const string& file_name) {
        auto rep = Response(code);
        rep.file_name = file_name;
        return rep;
    }

    static Response error(int code, const string& msg) {
        return Response(code, {{"message", msg}, {"code", code}});
    }

    static Response error(int code, const char* msg) {
        return Response(code, {{"message", msg}, {"code", code}});
    }

    template <typename Type>
    static Response not_found(Type msg) {
        return error(404, msg);
    }

    template <typename Type>
    static Response bad_request(Type msg) {
        return error(400, msg);
    }

    template <typename Type>
    static Response conflict(Type msg) {
        return error(409, msg);
    }

    template <typename Type>
    static Response internal_error(Type msg) {
        return error(500, msg);
    }

private:
    bool complete_{};
};

}}
